package com.huanxing.cloud.mall.api.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.huanxing.cloud.common.core.constant.CacheConstants;
import com.huanxing.cloud.common.core.constant.CommonConstants;
import com.huanxing.cloud.common.core.util.SnowflakeIdUtils;
import com.huanxing.cloud.mall.api.mapper.*;
import com.huanxing.cloud.mall.api.service.IOrderInfoService;
import com.huanxing.cloud.mall.api.utils.HxTokenHolder;
import com.huanxing.cloud.mall.common.constant.MallOrderConstants;
import com.huanxing.cloud.mall.common.dto.OrderInfoDTO;
import com.huanxing.cloud.mall.common.entity.*;
import com.huanxing.cloud.mall.common.enums.MallErrorCodeEnum;
import com.huanxing.cloud.mall.common.enums.OrderStatusEnum;
import lombok.AllArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * 订单
 *
 * @author lijx
 * @since 2022/3/7 14:18
 */
@Service
@AllArgsConstructor
public class OrderInfoServiceServiceImpl extends ServiceImpl<OrderInfoMapper, OrderInfo>
    implements IOrderInfoService {
  private final RedisTemplate<String, String> redisTemplate;
  private final OrderItemMapper orderItemMapper;
  private final UserAddressMapper userAddressMapper;
  private final OrderLogisticsMapper orderLogisticsMapper;
  private final ShoppingCartMapper shoppingCartMapper;
  private final GoodsSpuMapper goodsSpuMapper;
  private final GoodsSkuMapper goodsSkuMapper;
  private final GoodsSkuSpecsValueMapper goodsSkuSpecsValueMapper;

  @Override
  public IPage<OrderInfo> apiPage(Page page, OrderInfo orderInfo) {
    return baseMapper.selectApiPage(page, orderInfo);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public OrderInfo createOrder(OrderInfoDTO orderInfoDTO) {
    // 是否购物车下单
    boolean delShoppingCart =
        StrUtil.isNotBlank(orderInfoDTO.getCreateWay())
            && MallOrderConstants.ORDER_CREATE_WAY_1.equals(orderInfoDTO.getCreateWay());

    OrderInfo orderInfo = new OrderInfo();
    orderInfo.setAppraiseStatus(CommonConstants.NO);
    orderInfo.setOrderType(orderInfoDTO.getOrderType());
    orderInfo.setOrderNo(SnowflakeIdUtils.orderNo());
    orderInfo.setDeliveryWay(orderInfoDTO.getDeliveryWay());
    orderInfo.setPaymentType(orderInfoDTO.getPaymentType());
    orderInfo.setPayPrice(BigDecimal.ZERO);
    orderInfo.setRemark(orderInfoDTO.getRemark());
    orderInfo.setStatus(OrderStatusEnum.STATUS_1.getCode());
    orderInfo.setTotalPrice(BigDecimal.ZERO);
    orderInfo.setSalesPrice(BigDecimal.ZERO);
    orderInfo.setFreightPrice(BigDecimal.ZERO);
    orderInfo.setCouponPrice(BigDecimal.ZERO);
    orderInfo.setUserId(HxTokenHolder.getMallUserId());
    orderInfo.setPayStatus(CommonConstants.NO);
    List<OrderItem> orderItemList = new ArrayList<>();
    // 订单商品
    orderInfoDTO
        .getOrderItemList()
        .forEach(
            orderItem -> {
              orderItem.setSpecsInfo("");
              GoodsSpu goodsSpu = goodsSpuMapper.selectById(orderItem.getSpuId());
              // 只有已上架商品可以购买
              if (ObjectUtil.isNotNull(goodsSpu)
                  && CommonConstants.YES.equals(goodsSpu.getStatus())) {
                GoodsSku goodsSku = goodsSkuMapper.selectById(orderItem.getSkuId());
                if (ObjectUtil.isNotNull(goodsSku)) {
                  orderItem.setSpuName(goodsSpu.getName());
                  orderItem.setSpuId(goodsSpu.getId());
                  orderItem.setSkuId(goodsSku.getId());
                  List<GoodsSkuSpecsValue> goodsSkuSpecsValues =
                      goodsSkuSpecsValueMapper.selectBySkuId(goodsSku.getId());
                  goodsSkuSpecsValues.forEach(
                      goodsSkuSpecValue -> {
                        String specInfo = orderItem.getSpecsInfo();
                        specInfo = StrUtil.isNotBlank(specInfo) ? specInfo : "";
                        orderItem.setSpecsInfo(
                            specInfo + goodsSkuSpecValue.getSpecsValueName() + "；");
                      });
                  if (CollUtil.isNotEmpty(goodsSkuSpecsValues)) {
                    orderItem.setPicUrl(
                        StrUtil.isNotBlank(goodsSkuSpecsValues.get(0).getPicUrl())
                            ? goodsSkuSpecsValues.get(0).getPicUrl()
                            : goodsSpu.getSpuUrls().get(0));
                  }
                  orderItem.setSalesPrice(goodsSku.getSalesPrice());
                  orderItem.setTotalPrice(goodsSku.getSalesPrice());
                  // 校验参数 数量、金额
                  this.verifyParameter(orderItem, goodsSku);
                  orderItemList.add(orderItem);
                  orderInfo.setTotalPrice(
                      orderInfo
                          .getSalesPrice()
                          .add(
                              goodsSku
                                  .getSalesPrice()
                                  .multiply(BigDecimal.valueOf(orderItem.getBuyQuantity()))));
                  orderInfo.setSalesPrice(
                      orderInfo
                          .getSalesPrice()
                          .add(
                              goodsSku
                                  .getSalesPrice()
                                  .multiply(BigDecimal.valueOf(orderItem.getBuyQuantity()))));
                  orderInfo.setFreightPrice(
                      orderInfo.getFreightPrice().add(orderItem.getFreightPrice()));
                  orderInfo.setPayPrice(orderInfo.getPayPrice().add(orderItem.getPayPrice()));
                  orderInfo.setCouponPrice(
                      orderInfo.getCouponPrice().add(orderItem.getCouponPrice()));
                  // 扣减库存
                  goodsSku.setStock(goodsSku.getStock() - orderItem.getBuyQuantity());
                  goodsSkuMapper.updateById(goodsSku);

                  // 购物车下单清除购物车商品
                  if (delShoppingCart) {
                    shoppingCartMapper.delete(
                        Wrappers.<ShoppingCart>lambdaQuery()
                            .eq(ShoppingCart::getSpuId, orderItem.getSpuId())
                            .eq(ShoppingCart::getUserId, orderInfo.getUserId()));
                  }
                }
              }
            });
    if (MallOrderConstants.DELIVERY_WAY_1.equals(orderInfo.getDeliveryWay())) {
      // 保存物流信息
      OrderLogistics orderLogistics = this.saveOrderLogistics(orderInfoDTO);
      orderInfo.setOrderLogisticsId(orderLogistics.getId());
    }
    if (CollUtil.isEmpty(orderItemList)) {
      throw new RuntimeException(MallErrorCodeEnum.ERROR_60008.getMsg());
    }
    // 保存订单
    if (!super.save(orderInfo)) {
      throw new RuntimeException(MallErrorCodeEnum.ERROR_60007.getMsg());
    }
    // 保存子订单
    orderItemList.forEach(
        orderItem -> {
          orderItem.setOrderId(orderInfo.getId());
          orderItemMapper.insert(orderItem);
        });
    // 保存待支付订单到redis 30分钟 自动取消
    redisTemplate
        .opsForValue()
        .set(
            CacheConstants.MALL_ORDER_STATUS + orderInfo.getId(),
            orderInfo.getId(),
            1800,
            TimeUnit.SECONDS);
    return orderInfo;
  }
  /**
   * 效验订单参数
   *
   * @author lijx
   * @date 2022/6/11
   * @param orderItem
   * @param goodsSku
   * @return: void
   */
  public void verifyParameter(OrderItem orderItem, GoodsSku goodsSku) {
    if (orderItem.getBuyQuantity() < 1) {
      throw new RuntimeException("购买数量必须大于1：" + orderItem.getSpuName());
    }
    if (orderItem.getPayPrice().compareTo(BigDecimal.ZERO) < 0) {
      throw new RuntimeException("支付价格不能小于0：" + orderItem.getSpuName());
    }
    // 商品总单价 = 购买数量*单价
    BigDecimal salesPrice =
        goodsSku.getSalesPrice().multiply(BigDecimal.valueOf(orderItem.getBuyQuantity()));
    // 支付金额 =  商品总单价-商品优惠价+运费
    BigDecimal paySum =
        salesPrice
            .add(
                ObjectUtil.isNotNull(orderItem.getFreightPrice())
                    ? orderItem.getFreightPrice()
                    : BigDecimal.ZERO)
            .subtract(
                ObjectUtil.isNotNull(orderItem.getCouponPrice())
                    ? orderItem.getCouponPrice()
                    : BigDecimal.ZERO);
    if (paySum.compareTo(orderItem.getPayPrice()) != 0) {
      throw new RuntimeException("支付金额与实际支付金额不符：" + orderItem.getSpuName());
    }
  }
  /**
   * 保存物流信息
   *
   * @author lijx
   * @date 2022/6/11
   * @param orderInfoDTO
   * @return: com.huanxing.cloud.mall.common.entity.OrderLogistics
   */
  public OrderLogistics saveOrderLogistics(OrderInfoDTO orderInfoDTO) {
    UserAddress userAddress = userAddressMapper.selectById(orderInfoDTO.getUserAddressId());
    if (ObjectUtil.isNull(userAddress)) {
      throw new RuntimeException(MallErrorCodeEnum.ERROR_50002.getMsg());
    }
    // 保存物流信息
    OrderLogistics orderLogistics = new OrderLogistics();
    StringBuilder sb = new StringBuilder(userAddress.getProvinceName());
    sb.append(userAddress.getCityName())
        .append(userAddress.getCountyName())
        .append("-")
        .append(userAddress.getDetailAddress());
    orderLogistics.setAddresseeName(userAddress.getAddresseeName());
    orderLogistics.setDetailAddress(sb.toString());
    orderLogistics.setTelephone(userAddress.getTelephone());
    orderLogistics.setPostalCode(userAddress.getPostalCode());
    orderLogisticsMapper.insert(orderLogistics);
    return orderLogistics;
  }

  @Override
  public OrderInfo getOrderById(String id) {
    OrderInfo orderInfo = baseMapper.selectOrderById(id);
    if (OrderStatusEnum.STATUS_1.getCode().equals(orderInfo.getStatus())) {
      Long outTime = redisTemplate.getExpire(CacheConstants.MALL_ORDER_STATUS + orderInfo.getId());
      if (outTime != null && outTime > 0) {
        orderInfo.setOutTime(outTime);
      }
    }
    return orderInfo;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public boolean cancelOrder(OrderInfo orderInfo) {
    if (OrderStatusEnum.STATUS_1.getCode().equals(orderInfo.getStatus())) {
      orderInfo.setStatus(OrderStatusEnum.STATUS_11.getCode());
      orderInfo.setCancelTime(LocalDateTime.now());
      baseMapper.updateById(orderInfo);

      List<OrderItem> orderItemList =
          orderItemMapper.selectList(
              Wrappers.<OrderItem>lambdaQuery().eq(OrderItem::getOrderId, orderInfo.getId()));
      orderItemList.forEach(
          orderItem -> {
            GoodsSku goodsSku = goodsSkuMapper.selectById(orderItem.getSpuId());
            if (ObjectUtil.isNotNull(goodsSku)) {
              // 回滚库存
              goodsSku.setStock(goodsSku.getStock() + orderItem.getBuyQuantity());
              goodsSkuMapper.updateById(goodsSku);
            }
          });
    }
    return Boolean.TRUE;
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void notifyOrder(OrderInfo orderInfo) {
    if (orderInfo.getPayStatus().equals(CommonConstants.NO)) {
      orderInfo.setStatus(OrderStatusEnum.STATUS_2.getCode());
      orderInfo.setPayStatus(CommonConstants.YES);
      baseMapper.updateById(orderInfo);
      List<OrderItem> orderItemList =
          orderItemMapper.selectList(
              Wrappers.<OrderItem>lambdaQuery().eq(OrderItem::getOrderId, orderInfo.getId()));
      orderItemList.forEach(
          orderItem -> {
            GoodsSpu goodsSpu = goodsSpuMapper.selectById(orderItem.getSpuId());
            if (ObjectUtil.isNotNull(goodsSpu)) {
              // 增加销量
              goodsSpu.setSalesVolume(goodsSpu.getSalesVolume() + orderItem.getBuyQuantity());
              goodsSpuMapper.updateById(goodsSpu);
            }
          });
    }
  }

  @Override
  public boolean receiveOrder(OrderInfo orderInfo) {
    if (OrderStatusEnum.STATUS_3.getCode().equals(orderInfo.getStatus())) {
      orderInfo.setReceiverTime(LocalDateTime.now());
      orderInfo.setStatus(OrderStatusEnum.STATUS_4.getCode());
      baseMapper.updateById(orderInfo);
    }
    return Boolean.TRUE;
  }
}
